/* 
 * File:   Http.cpp
 * Author: tzl 
 * 
 * Created on July 1, 2011, 11:45 AM
 */
#include "stdafx.h"
#include <utility>
#include <vector>
#include "Http.h"

HttpReqBuilder::~HttpReqBuilder()
{
	if(_data)
	{
		free(_data);
		_data = NULL;
	}
}

bool HttpReqBuilder::setReqInfo(const std::string& reqType, const std::string& host,
	const std::string& uri)
{
	assert(reqType == "POST" || reqType == "GET");
	_reqType = reqType;
	_host = host;
	_uri = uri;
	_data = NULL;
	_datalen = 0;
	return true;
}

bool HttpReqBuilder::addHeader(const std::string& hdrname, const std::string& hdrvalue)
{
	_headers.push_back(Headers::value_type(hdrname, hdrvalue));
	return true;
}

bool HttpReqBuilder::setData(const char* databuf, size_t len)
{
	_data = (char* )malloc(len);
	memcpy(_data, databuf, len);
	_datalen = len;
	return true;
}

bool HttpReqBuilder::buildPacket(char* outbuf, size_t len)
{
	assert(false);
	// no implemenation
	return false;
}

size_t HttpReqBuilder::computePacketSize() const
{
	size_t buflen = (_headers.size() + 2) * 1024 + (sizeof(HTTP_CRLF) - 1) * 2 + _datalen;
	return buflen;
}

std::string HttpReqBuilder::buildPacket()
{
	size_t buflen = computePacketSize();
	char* buf = (char* )malloc(buflen);

	// req method
	int len = snprintf(buf, buflen, "%s %s HTTP/1.1" HTTP_CRLF, _reqType.c_str(), 
		_uri.c_str());
	
	size_t pos = len;

	// Host header
	len = snprintf(buf + pos, buflen - pos, "Host: %s" HTTP_CRLF,
			_host.c_str());
	pos += len;
	
	for (size_t i = 0; i < _headers.size(); i ++) {

		len = snprintf(buf + pos, buflen - pos, "%s: %s" HTTP_CRLF,
			_headers[i].first.c_str(), _headers[i].second.c_str());
		pos += len;
	}

	// Content-Length header
	len = snprintf(buf + pos, buflen - pos, "Content-Length: %ld" HTTP_CRLF,
			_datalen);
	pos += len;

	memcpy(buf + pos, HTTP_CRLF, sizeof(HTTP_CRLF) - 1);
	pos += sizeof(HTTP_CRLF) - 1;
	memcpy(buf + pos, _data, _datalen);
	std::string result = buf;
	free(buf);
	return result;
}

////////////////////////////////////////////////////////////////////////////

HttpResponseParser::HttpResponseParser(size_t len /* = MAX_HTTP_RESP */ )
{
	_buf = (char* )malloc(len);
	_len = len;
	_state = NeedHeader;
	_pos = 0;
	_hdrLen = 0;
	_dataLen = 0;
}

HttpResponseParser::~HttpResponseParser()
{
	free(_buf);
}

bool HttpResponseParser::parse(const char* buf, size_t len)
{
	assert(_state != RespOK);
	
	if (_pos + len > _len) {

		// FIXME: buffer size will be dynamic
		_pos = 0;
		// assert(false);
		return false;
	}
	
	memcpy(_buf + _pos, buf, len);
	_pos += len;
	_buf[_pos] = 0;

	if (_state == NeedHeader) {

		char* p = strstr(_buf, HTTP_CRLF HTTP_CRLF);
		if (p != NULL) {
			_state = NeedData;
			_hdrLen = p - _buf;

			p = strstr(_buf, HTTP_CONTENT_LENGTH);
			if (p == NULL) {
				//增加对chunked模式的支持
				p = strstr(_buf, HTTP_TRANSFER_ENCODING_CHUNK);
				if(p)
				{
					p = strstr(_buf, HTTP_CRLF HTTP_CRLF) + 4; //4 is for skipping "\r\n\r\n"
					size_t trunkSize = 0;		
					char *pd = HTTP_CRLF;
					while(1)
					{
						trunkSize = strtoul(p,&pd,16); //string hex to unsigned long
						if(0 == trunkSize)
						{
							*p = 0;//cut off tail
							break;
						}
						_dataLen += trunkSize;
						char *q = strstr(p,HTTP_CRLF) + 2; //2 is for skipping "\r\n"
						size_t pos = q - _buf;
						memmove(p,q,len-pos); //erase front trunk
						p += trunkSize;
						q = p + 2;//2 is for skipping "\r\n"
						pos = q - _buf;
						memmove(p,q,len-pos); //erase end trunk
					}
				}
				else
				{
					_dataLen = 0;
					_state = RespOK;
					return true;
				}

			} else {
				_dataLen = atol(p + sizeof(HTTP_CONTENT_LENGTH) - 1 /* ending zero */
					+ 2 /* colon & space */);
			}
		}
	}

	if (_state == NeedData) {

		assert(_hdrLen);
		// assert(_dataLen);

		if (_pos - _hdrLen >= _dataLen) {
			_state = RespOK;
			return true;
		}
	}

	return false;
}

size_t HttpResponseParser::getResponseSize() const
{
	return _hdrLen + _dataLen + sizeof(HTTP_CRLF HTTP_CRLF) - 1;
}

size_t HttpResponseParser::getResponse(char* buf, size_t buflen, size_t* datapos)
{
	size_t respSize = getResponseSize();
	if (buflen <= respSize) {
		assert(false);
		return 0;
	}

	memcpy(buf, _buf, respSize);
	memmove(_buf, _buf + respSize, _pos - respSize);

	if (datapos)
		*datapos = _hdrLen + sizeof(HTTP_CRLF HTTP_CRLF) - 1;
	
	_pos -= respSize;
	_hdrLen = 0;
	_dataLen = 0;

	_state = NeedHeader;
	return respSize;
}
